Eureka 服务注册与消费(超详细)

作者: 李多多 日期: 2020-06-24
Spring Cloud
Eureka 服务注册与消费(超详细)

1. 服务注册

服务注册就是把一个微服务注册到 Eureka Server 上,当其它服务需要调用该服务时,只需从 Eureka Server 上查询该服务的信息即可。

下面创建一个 provider,作为服务提供者,创建项目时,选择 Eureka Client 依赖,这样,当服务创建成功后,简单配置一下,就可以被注册到 Eureka Server 上了。

image.png

项目创建成功后,只需在 application.properties 中配置一下项目的注册地址即可。注册地址的配置,和 Eureka Server 集群的配置很像。配置如下:

spring.application.name=provider
server.port=1113
eureka.client.service-url.defaultZone=http://127.0.0.1:1111/eureka

然后启动 Eureka Server ,待服务注册中心启动后,再启动 provider。两者启动成功后,浏览器输入 http://localhost:1111,就可以看到 provider 的注册信息,如下:

image.png

2. 服务消费

2.1 基本用法

首先提供者创建访问接口,然后创建消费者,消费这个访问接口 (在 provider 中提供一个 hello 接口):

@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello 你好";
}
}

然后,创建一个 consumer 项目,consumer 去消费 provider 提供的接口。consumer 想要获取到 provider 这个接口的地址,他就去 Eureka Server 中查询,如果直接在 consumer 中写死 provider 地址,意味着这两个服务之间的耦合度就太高了,我们要降低耦合度。先看一个写死的调用:

创建一个 consumer 项目,添加 web 和 eureka client 依赖:

image.png

创建成功后,在 application.properties 中配置一下项目的注册地址。配置如下:

spring.application.name=consumer
server.port=1115
eureka.client.service-url.defaultZone=http://127.0.0.1:1111/eureka

配置完成后,假设我们在 consumer 中调用 provider 提供的服务,我们可以直接将调用写死,就是说,整个调用过程不会涉及到 Eureka Server。

/** 测试死的地址访问
* */
@GetMapping("/hello1")
public String hello1() throws MalformedURLException {
HttpURLConnection con = null;
try {
URL url = new URL("http://127.0.0.1:1113/hello");//这里测试写死provider地址,耦合度太高
con = (HttpURLConnection) url.openConnection();
if(con.getResponseCode() == 200){
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(con.getInputStream()));
String s = bufferedReader.readLine();
bufferedReader.close();
return s;

}

} catch (IOException e) {
e.printStackTrace();
}
return "error";
}

浏览器访问如下,调用成功:

image.png

这是一段利用了 HttpURLConnection 来发起的请求,请求中的 provider 地址写死了,意味着 provider 和 consumer 高度绑定在一起,耦合度太高。

2.2 消费的接口改造

要改造它,我们借助 Eureka Client 提供的 DiscoveryClient 工具,根据服务名从 Eureka Server 上查询到一个服务的详细地址,改造后的代码如下:

@Autowired
DiscoveryClient discoveryClient;

@GetMapping("/hello2")
public String hello2(){
    List<ServiceInstance> list = discoveryClient.getInstances("provider");
    ServiceInstance serviceInstance = list.get(0);
    String host = serviceInstance.getHost();
    int port = serviceInstance.getPort();
    StringBuffer sb = new StringBuffer();
    sb.append("http://")
            .append(host)
            .append(":")
            .append(port)
            .append("/hello");
    HttpURLConnection connection = null;
try {
            
            URL url = new URL(sb.toString());
            connection = (HttpURLConnection) url.openConnection();

            if (200 == connection.getResponseCode()) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                
                String s = reader.readLine();
                reader.close(); 
                return s;
            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    return "error";
}

重新启动项目,浏览器访问如下:

image.png

2.3 集群化部署和简单的负载均衡

DiscoveryClient 查询到的服务列表是一个集合,因为服务在部署的过程中,可能是集群部署,集合中的每一项就是一个实例。

集群化部署:

  1. 修改 provider 中的 hello 接口【因为要启动多个 provider 实例,多个实例端口则不同,用端口直接区分每个 provider 提供的服务】:
    public class HelloController {
    @Value("${server.port}")
    Integer port;

    @GetMapping("/hello")
    public String hello(){
    return "hello 你好" + port;
    }
    }
    修改完成后,对 provider 进行打包,打包后在 IDEA 命令行启动两个 provider 实例 【集群】:
    java -jar provider-0.0.1-SNAPSHOT.jar --server.port=1113
    java -jar provider-0.0.1-SNAPSHOT.jar --server.port=1116
    启动完成后,访问注册中心看有没有注册上来。然后再去调用 provider ,DiscoveryClient 集合中,获取到的就不是一个实例了,而是两个实例。这里我们可以手动实现一个负载均衡:
    int count = 0;
    @GetMapping("/hello3")
    public String hello3(){
    List<ServiceInstance> list = discoveryClient.getInstances("provider");
    ServiceInstance serviceInstance = list.get((count++) % list.size());
    String host = serviceInstance.getHost();
    int port = serviceInstance.getPort();
    StringBuffer sb = new StringBuffer();
    sb.append("http://")
    .append(host)
    .append(":")
    .append(port)
    .append("/hello");
    HttpURLConnection connection = null;
    try {
    URL url = new URL(sb.toString());
    connection = (HttpURLConnection) url.openConnection();

    if (200 == connection.getResponseCode()) {
    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
    String s = reader.readLine();
    reader.close();
    return s;
    }

    } catch (MalformedURLException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }

    }
    return "error";
    }
    在 target 文件上右键 open in terminal 分别输入 java -jar provider-0.0.1-SNAPSHOT.jar –server.port=1113 、

java -jar provider-0.0.1-SNAPSHOT.jar –server.port=1116 启动两个 provider ,打开 Eureka Server 如下图:已经注册成功。

image.png

接着访问 http://localhost:1115/hello3,如下图【多次访问端口已改变】,已实现**线性负载均衡**:

image.png

2.4 升级改造

主要从两方面动手:

http 调用:spring 提供 RestTemplate
负载均衡 : Ribbon

2.4.1 http 调用

用 spring 提供 RestTemplate 来实现,以下提供一个实例:

@SpringBootApplication
public class ConsumerApplication {

public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}

@Bean
RestTemplate restTemplateOne(){
return new RestTemplate();
}
}

将 HttpURLConnection 以及下面的那一段用 RestTemplate 替换,一行代码实现 http 调用,如下:

@Autowired
DiscoveryClient discoveryClient;

@Autowired
@Qualifier("restTemplateOne")
RestTemplate restTemplateOne;

@GetMapping("/hello2")
public String hello2(){
List<ServiceInstance> list = discoveryClient.getInstances("provider");
ServiceInstance serviceInstance = list.get(0);
String host = serviceInstance.getHost();
int port = serviceInstance.getPort();
StringBuffer sb = new StringBuffer();
sb.append("http://")
.append(host)
.append(":")
.append(port)
.append("/hello");
String s = restTemplateOne.getForObject(sb.toString(), String.class);
return s;
}

2.4.2 开启负载均衡

使用 Ribbon 来快速实现负载均衡。只需要给 RestTemplate 实例添加一个 @LoadBalanced 注解,开启负载均衡:

@Bean
@LoadBalanced //【客户端负载均衡】
RestTemplate restTemplate(){
return new RestTemplate();
}

调用代码如下:

@Autowired
@Qualifier("restTemplate")
RestTemplate restTemplate;

@GetMapping("/hello3")
public String hello3(){
//restTemplate有负载均衡功能,直接给服务名调用就行
return restTemplate.getForObject("http://provider/hello",String.class);
}

接着访问 http://localhost:1115/hello3,如下图:
image.png
image.png

给 RestTemplate 添加负载均衡的注解也可以理解为 RestTemplate = http 调用 + 负载均衡

项目实例地址:https://github.com/astronger/springcloud-simple-samples